#include <iostream>
#include <fstream>
#include "snetwork/NluHttpCreate.h"
#include "snetwork/NluEvent.h"
#include "processlog.h"
#include "snetwork/CreateThread.h"
#include "snetwork/WorkerProcess.h"
#include "snetwork/TDateParse.h"
#include "snetwork/NluList.h"
#include "standard/nstring.h"
#include "standard/nfile.h"
#include "snetwork/gzlog.h"
#include "readfile.h"

#define RFC1123FMT "%a, %d %b %Y %H:%M:%S GMT"

namespace Sock
{   
    CNluHttpCreate *CNluHttpCreate::m_instance = NULL;

    CNluHttpCreate *CNluHttpCreate::instance()
    {
        if (m_instance==NULL) {
            m_instance = new CNluHttpCreate();
        }
        return m_instance;
    }
    
    CNluHttpCreate::CNluHttpCreate()
    {
        
    }
    
    int CNluHttpCreate::execute(void* parr, nl_http_request_t *r)    
    {
        try {            
            m_par = (paramArr *) parr;
            
            switch(r->method)
            {
            case NL_HTTP_GET:
                this->findClassMap(r);
                break;
            default:break;
            }
		}
		catch (CThrowException e) {
			llog("Error: %s", e.getMessage().c_str());
		}
		
        return 0;
    }
    
    /**
     *  ----- 304 -----

     *  304 如果客服端已经完成一个有条件的请求并且请求是允许的， 但是这个文档并没有改变， 服务器应该返回304状态码。
     *  304 状态码一定不能包含信息主体， 从而通常通过一个头字段后的第一个空行结束。     
     */
    void CNluHttpCreate::findClassMap( nl_http_request_t *r )
    {          
        bool is_gzip = false;
        n_status = 200;
        int NUM_SIZE = 100*1024;  
        
        m_filename.clear();      

        std::string  public_path = get_config("root", "ROOT_PATH") + get_config("path","PUBLIC_PATH");
        if (public_path.substr(public_path.length()-1) != "/") {
            public_path.append(1, '/');
        }
        
        (*(m_par->action)).clear();
        if (strcmp(r->uri_start, "") == 0) {
            throw CThrowException("invaild http headers");
        }
        
        (*(m_par->action)).append(r->uri_start+1);                        
        std::string::size_type pos= (*(m_par->action)).find('?', 0);

        if (pos != std::string::npos) {
            *(m_par->action) = (*(m_par->action)).substr(0, pos);
        }

        if ( (*(m_par->action)).compare("") == 0 ) {
            *(m_par->action) = "index";
        }
        
		ClassMap::iterator itf = theDynamicClassMap.find(*(m_par->action));	
		
		m_filename.append(public_path);
        m_filename.append(*(m_par->action));

		(*(m_par->sendArr)).clear();
		
		if (file_exists(m_filename.c_str())) {
		    llog( "file : \"%s\"" , m_filename.c_str() );	
			int infd = open(m_filename.c_str(), O_RDONLY); //orginal
			
			FILE *fp=fopen(m_filename.c_str(), "rb");
			long fsize = getfilesize(fp);
			fclose(fp);
									
			// send header
			std::string type;
		    const char *mine_type = ".txt";
			    
			type = this->get_mime_type( mine_type, strlen(mine_type) );			
            bool not_modified = this->http_headers( n_status, type, fsize, is_gzip, m_filename.c_str());            

            //------------------send header------------------------//
            memset(r->header_in->start, 0, HEADER_INIT_LEN);
		    strncpy(r->header_in->start, n_response_header.c_str(), n_response_header.length());
		    r->header_in->in_file = 0;
		    r->header_in->last  = r->header_in->start;
		    r->header_in->end   = r->header_in->start + n_response_header.length();

		    r->out = CNluList::instance()->listInsert_L(r->out_head, r->out, r->header_in, n_response_header.length());
	        //llog("r->out->buf ->start:\n%s", r->out->buf->start);
	        llog("header size:%d", n_response_header.length());
	        
	        //------------------send body--------------------------//
	        r->header_in->file->fd = infd;
			r->header_in->file->name = m_filename.c_str();
			r->header_in->in_file = 1;
			memset(r->header_in->start, 0, HEADER_INIT_LEN);
			r->header_in->last  = r->header_in->start;
		    r->header_in->end   = r->header_in->start + fsize;
		    r->out = CNluList::instance()->listInsert_L(r->out_head, r->out, r->header_in, fsize, r->header_in->file);
		    llog("body size:%ld", fsize);
		    
            llog("r->out->buf->file->fd :%d", r->out->buf->file->fd );			    
		    	
	        /*
			//if file has changed
			if (!not_modified) {
			    unsigned long readnum = 0;
			    memset(r->header_in->start, 0, HEADER_INIT_LEN);
			    
		    	while ( (readnum = fread(r->header_in->start, 1, HEADER_INIT_LEN, fp)) > 0) {
		            r->header_in->last  = r->header_in->start;
		            r->header_in->end   = r->header_in->start + readnum;		             
		            r->out = CNluList::instance()->listInsert_L(r->out_head, r->out, r->header_in, readnum);//here error
		            llog("r->out->buf->start:\n%s", r->out->buf->start);
		            memset(r->header_in->start, 0, HEADER_INIT_LEN);   			    	    
		        }
			}*/
			
			//fclose(fp);			
			m_par->is_static = true;
		}
		else if ( itf != theDynamicClassMap.end() ) {
		    llog( "You are access \"%s\"\n", (*(m_par->action)).c_str() );	
			this->_call_method(itf, r);
			m_par->is_static = false;
		}
		else {
		    llog( "file : \"%s\" not found\n", m_filename.c_str() );
			this->_not_found();
		}
	}

	//调用action
	int CNluHttpCreate::_call_method(ClassMap::iterator itf, nl_http_request_t *r)
	{
		n_status = 200;

		funcCreateObject pCreateFunc = itf->second;
		Threads::ThreadObject* pDynaObj = pCreateFunc(NULL);
		
		m_par->rbArr    = &n_rbArr;
		m_par->textinfo = &n_textinfo;
		m_par->FILES    = &n_FILES;
		m_par->pobj     = pDynaObj;
		
        CreateThread::instance(NULL)->execute( m_par );

#if defined(_MSC_VER)
		gbktoutf8(m_par->sendArr);
#endif

        //pre to send
		bool is_gzip = false;
		std::string mine_type = "text/html";			
        this->pre_data(mine_type, is_gzip, r);  

        //now send 
        long nLeaveByte = this->send_data(mine_type, r);


		if ( (*(m_par->textinfo))["found"].compare("0") == 0 ) {
			this->_not_found();
		}
		return 0;
	}
	
	int CNluHttpCreate::pre_data(std::string mine_type, bool is_gzip, nl_http_request_t *r)
    {
        std::string type;
        long int context_len = 0;

        switch(r->method)
        {
        case NL_HTTP_GET:
        
            if ( ! (*(m_par->sendArr)).empty() ) {
                context_len = (*(m_par->sendArr))[0].length();
            }
            this->http_headers( n_status, mine_type, context_len, is_gzip);
            
            break;
        
        default:break;
        }   
        
        return 0;
    }
               
    /**
     * send data
     */
	ssize_t CNluHttpCreate::send_data(std::string mine_type, nl_http_request_t *r)
	{			
		int sockfd = m_par->sockfd;
		vecStr* sendArr = m_par->sendArr;		
		ssize_t cnt_len;
		std::string base_head;
		std::string tmp_head;		         			
		std::string outs, sends;		
				
		base_head.erase();
		base_head = Sock::n_response_header;
		tmp_head.erase();			
		 				
		if ( mine_type.find("text/html", 0) != std::string::npos ) {
		    vecStr::iterator vtor;
		    vtor = (*sendArr).begin();
		    
		    for (; vtor != (*sendArr).end(); vtor++) {
			    std::string buf_len;
			    buf_len.clear();
			    buf_len.append( decToHex((*vtor).length()));
			    
			    if (!buf_len.empty()) {
				    tmp_head.append(buf_len.c_str(), buf_len.length());
				    tmp_head.append(WRAPCHAR);
				    if (!((*vtor).empty())) {
					    tmp_head.append((*vtor).c_str(), (*vtor).length());
				    }
				    tmp_head.append(WRAPCHAR);
			    }
		    }
				
		    tmp_head.append("0");
		    tmp_head.append(WRAPCHAR);
		    tmp_head.append(WRAPCHAR);
		}
		else {
		    tmp_head.append(((*sendArr)[0]).c_str(), ((*sendArr)[0]).length());	
		    tmp_head.append(WRAPCHAR);
		    tmp_head.append(WRAPCHAR);
		}
		
		sends = base_head+tmp_head;
		long nLeaveByte = 0;		
		
		llog("sends.length(): %d,  HEADER_INIT_LEN: %d", sends.length(), HEADER_INIT_LEN);
		memset(r->header_in->start, 0, HEADER_INIT_LEN);
		strncpy(r->header_in->start, sends.c_str(), sends.length());
		r->header_in->last  = r->header_in->start;
		r->header_in->end   = r->header_in->start + sends.length();
		
		// write to client
		//nLeaveByte = this->HttpWriteResponseHeader(r);
		r->header_in->in_file = 0;
		r->out = CNluList::instance()->listInsert_L(r->out_head, r->out, r->header_in, sends.length());

		return nLeaveByte;
	}	     
		
	int CNluHttpCreate::_not_found()
	{
		n_status = 404;

		(*(m_par->sendArr)).clear();
		if ( m_par->method.compare("NORMAL") == 0 ) {
			(*(m_par->sendArr)).push_back("statu=-1&message=action%20%50not&%20found");
        }
        else if ( m_par->method.compare("GET") == 0 || m_par->method.compare("POST") == 0 ) {
			std::string  app_path = get_config("root", "ROOT_PATH") + get_config("path","VIEW_PATH");
			std::string s, filename;			
			std::ifstream readf;
			s.clear();
			
			filename = app_path + "/404.htm";			
			if (file_exists(filename.c_str())) {
				readf.open(filename.c_str());
				load_file(s, readf);
				readf.close();
			}
			(*(m_par->sendArr)).push_back(s);
        }
            
		return 0;
	}

	
	void CNluHttpCreate::normal_headers( int status, std::string mime_type )
    {
        std::string header;
        header.clear(); 
         
        header.append("SERVER/1.1 " + itos(status) + " OK");
        header.append(WRAPCHAR);
        header.append("Content-Type: " + mime_type + "; charset=utf-8");
        header.append(WRAPCHAR);
        header.append("Transfer-Encoding: chunked");
        header.append(WRAPCHAR);
        header.append("Connection: keep-alive");
        header.append(WRAPCHAR);
        header.append(WRAPCHAR);
        
        n_response_header = header;
    }
    
    bool CNluHttpCreate::http_headers( int status, std::string mime_type, long int u_len, bool is_gzip)
    {
        return this->http_headers( status, mime_type, u_len, is_gzip, NULL);
    }       
    
    /**
    //Last-Modified: Fri, 02 Mar 2012 06:39:55 GMT
    */
    bool CNluHttpCreate::http_headers( int status, std::string mime_type, 
                                        long int u_len, bool is_gzip, const char *path)
    {
        std::string header;
        char timebuf[100];
        time_t now;
        time_t if_m = 0;
        time_t last_m = 0;
        bool flag = false;
        
        //if file not has changed set true or set false
        bool not_modified = false;
        now = time( NULL );        
        header.clear();         
        
        // from browser
        if (!n_head_arr["If-Modified-Since"].empty()) {
            std::string::size_type pos= n_head_arr["If-Modified-Since"].find(",", 1);
            if (pos != std::string::npos) {
                if_m = tdate_parse( n_head_arr["If-Modified-Since"].substr(pos+2).c_str() );
            }
        }
        
        // create in server
        memset( timebuf, 0, sizeof(timebuf));   
#if defined(__GNUC__)        
        struct stat st;        
        if (path != NULL && stat(path, &st) == 0) {
            last_m = st.st_mtim.tv_sec;            
            strftime( timebuf, sizeof(timebuf), RFC1123FMT, gmtime( &last_m ) );
        }
        else {
            strftime( timebuf, sizeof(timebuf), RFC1123FMT, gmtime( &now ) );
        }
#elif defined(_MSC_VER)
        strftime( timebuf, sizeof(timebuf), RFC1123FMT, gmtime( &now ) );        
#endif   
        
        if (if_m && if_m == last_m) {//Not Modified
            not_modified = true;
            header.append("HTTP/1.1 304 OK");
        }
        else {
            not_modified = false;
            header.append("HTTP/1.1 " + itos(status) + " OK");
        }                        
        header.append(WRAPCHAR);
        header.append("Server: httpd/1.0.0");
        header.append(WRAPCHAR); 
               
        header.append("Last-Modified: ");
        header.append(timebuf, strlen(timebuf));
        header.append(WRAPCHAR);                      
        
        memset(timebuf, 0, sizeof(timebuf));
        strftime( timebuf, sizeof(timebuf), RFC1123FMT, gmtime( &now ) );
        header.append("Date: ");
        header.append(timebuf, strlen(timebuf));
        header.append(WRAPCHAR);    
                                   
        header.append("Content-Type: " + mime_type); //charset=utf-8
        header.append(WRAPCHAR);
        
        if ( mime_type.find("text/html", 0) != std::string::npos ) {
            header.append("Transfer-Encoding: chunked");                                      
            header.append(WRAPCHAR);
        }
        else {
            std::string s_len = ultos(u_len);
            header.append("Content-Length: " + s_len);
            header.append(WRAPCHAR);     
        }

        header.append("Connection: keep-alive");
        header.append(WRAPCHAR);
        
        this->set_cookie(header);        
        /*
        if (is_gzip) {            
            header.append("Content-Encoding: gzip");
            header.append(WRAPCHAR);
        }  
        */ 
        
        header.append("Vary: Accept-Encoding");
        header.append(WRAPCHAR);
        header.append(WRAPCHAR);
                
        n_response_header = header;
        //llog(" n_response_header: %s ", n_response_header.c_str());
        
        return not_modified;
    }
        
	std::string CNluHttpCreate::get_mime_type( const char *namech, int len )
    {
        int n;
        char dot[10];
       
        memset(dot, 0, sizeof(dot));
        for(n=1; n<len&&n<9; n++) {
            dot[n-1] = namech[n];
        }
        
        if (namech[0] != '.') {//no found .
            return "text/plain; charset=utf-8";
        }
        else {
            dot[n+1] = '\0';
        }
        
        if ( strcmp( dot, ".html" ) == 0 || strcmp( dot, ".htm" ) == 0 )
	        return "text/html; charset=utf-8";
	        
	    if ( strcmp( dot, ".txt" ) == 0 )
	        return "text/plain; charset=utf-8";    

        if ( strcmp( dot, ".jpg" ) == 0 || strcmp( dot, ".jpeg" ) == 0 )
	        return "image/jpeg";

        if ( strcmp( dot, ".gif" ) == 0 )
	        return "image/gif";
	        
        if ( strcmp( dot, ".png" ) == 0 )
	        return "image/png";
	        
        if ( strcmp( dot, ".css" ) == 0 )
	        return "text/css";
	        
	    if ( strcmp( dot, ".js" ) == 0 )
	        return "application/x-javascript; charset=utf-8";    
	        
	    if ( strcmp( dot, ".swf" ) == 0 )
	        return "application/x-shockwave-flash";
	        
	    if ( strcmp( dot, ".flv" ) == 0 )
	        return "video/x-flv";    

        if ( strcmp( dot, ".au" ) == 0 )
	        return "audio/basic";
	        
        if ( strcmp( dot, ".wav" ) == 0 )
	        return "audio/wav";
	        
        if ( strcmp( dot, ".avi" ) == 0 )
	        return "video/x-msvideo";
	        
        if ( strcmp( dot, ".mov" ) == 0 || strcmp( dot, ".qt" ) == 0 )
	        return "video/quicktime";
	        
        if ( strcmp( dot, ".mpeg" ) == 0 || strcmp( dot, ".mpe" ) == 0 )
	        return "video/mpeg";
	        
        if ( strcmp( dot, ".vrml" ) == 0 || strcmp( dot, ".wrl" ) == 0 )
	        return "model/vrml";
	        
        if ( strcmp( dot, ".midi" ) == 0 || strcmp( dot, ".mid" ) == 0 )
	        return "audio/midi";
	        
        if ( strcmp( dot, ".mp3" ) == 0 )
	        return "audio/mpeg";
	        
        if ( strcmp( dot, ".ogg" ) == 0 )
	        return "application/ogg";
	        
        if ( strcmp( dot, ".pac" ) == 0 )
	        return "application/x-ns-proxy-autoconfig";
	        
        return "text/html; charset=utf-8";
    }
    
     /** 
     * 设置 cookie
     */
    void CNluHttpCreate::set_cookie(std::string &header)
    {
        mapStrStr::iterator cook;        
        std::string cokstr;
        
        cokstr.clear();
        for (cook = n_CookieSend.begin(); cook != n_CookieSend.end(); cook++) {                         
            if ( (cook->first).find("|") != std::string::npos ) {
                vecStr keyarr = explode("|", cook->first);                                
                vecStr keyval = explode("|", cook->second);
                
                if (!n_CookieRecv["HTTPDSID"].empty() && keyarr[0].compare("HTTPDSID") == 0 ) {
                    continue;
                }                               
                
                if (keyarr.size() != keyval.size()) {
                    cokstr += cook->first + "=" + cook->second + "\r";
                }
                else {
                    vecStr::iterator keyor;   
                    int num=0;
                    
                    for (keyor = keyarr.begin(); keyor != keyarr.end(); keyor++, num++) {
                        cokstr += *keyor + "=" + keyval[num] + ";";
                    }
          
                    cokstr = cokstr.substr(0, cokstr.length()-1);
                    cokstr += "\r";
                }                
            }
            else {
                if (!n_CookieRecv["HTTPDSID"].empty() && cook->first.compare("HTTPDSID") == 0 ) {
                    // not find |
                    continue;
                }  
                cokstr += cook->first + "=" + cook->second + "\r";
            }
        }                   
                   
        cokstr = cokstr.substr(0, cokstr.length()-1);
        header.append("Set-Cookie: " + cokstr );// ltos(time( NULL ))        
        header.append(WRAPCHAR); 
    }
    
    /**
     * parse url
     */
    void CNluHttpCreate::parseGetArr(void *para)
    {
        std::string::size_type pos;
        std::string query;
        PGetParam *par = (PGetParam *) para;        
        vecStr vact;
		vact= explode(" ", *(par->head_arr));

        query.clear();
        if (!vact.empty()) {
            if (vact[0].compare("/") == 0) {
                *(par->action) = "index";
            }
            else {
                if ( (pos=vact[0].find("?")) != std::string::npos ) {//find ?
                    *(par->action) = vact[0].substr(1,pos-1);                    
                    query = vact[0].substr(pos+1);
                    getArr( *(par->tget), query );
                }
                else {
                    *(par->action) = vact[0].substr(1); 
                }

                if ((*(par->action)).compare("") == 0) {
                    *(par->action) = "index";
                }    
            }
        }
    }   
    
    long CNluHttpCreate::getfilesize(FILE *stream)
    {
        long curpos, length;
        
        curpos = ftell(stream);
        fseek(stream, 0L, SEEK_END);
        length = ftell(stream);
        fseek(stream, curpos, SEEK_SET);
        
        return length;
    }
     
    /**
     * 显示在线客户端
     */
    void CNluHttpCreate::_showclient()
    {        
        std::cout<<"client amount: " << Sock::n_fd_count << "\n";
        
        for (int i = 0; i < MAX_FD_NUM; i++) {
             std::cout<<"["<<i<<"]:"<<Sock::n_fd_arr[i]<<" ";
        }
        
        std::cout<<"\n\n";
    }
    
	int CNluHttpCreate::mkstemp (char *tmpl)
    {
	    return 1;
    }    
    
    long int CNluHttpCreate::SendStaticFile(const char *sends, long len)
    {
        long int iSize = len;
		long int nLeaveByte = iSize;
		long int nCount = 0;
		long int iNumByte = 0;
       
        fd_set wfdsr;
        struct timeval tv;
        
		while (nLeaveByte > 0) {
		    if ( !(this->SocketConnected(m_par->sockfd)) ) {
		        break;
		    }		    
                
	        iNumByte = send(m_par->sockfd, sends + nCount, nLeaveByte, 0);
	        llog("iNumByte : %ld", iNumByte);
	        if (iNumByte < 0) {// network bad?
	            close(m_par->sockfd);
                m_par->sockfd = -1;                    
		        break;
	        }
	        else if (iNumByte == 0) {// network break?
	            close(m_par->sockfd);
                m_par->sockfd = -1;
		        break;
	        }
	        else {
		        nCount += iNumByte;
		        nLeaveByte -= iNumByte;
	        }		
		}
		
		return nLeaveByte;
    } 
    
	bool CNluHttpCreate::SocketConnected(int sock)
    {
        int optval , optlen = sizeof(int);        
        getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*) &optval, (socklen_t *)&optlen);
        
        switch(optval) {
            case 0:
                return true;
            case ECONNREFUSED:
                break;
            //case EHOSTNUMREACH:
        }
        
        return false;
    }  

}
